from numpy import random
from collections import namedtuple
from itertools import tee
from ways import *
from search import aStarSearch



class Entry(object):
	"""docstring for Entry"""
	def __init__(self, path):
		self.path = path
		self.times=[]

PathLen = namedtuple('PathLen', ['idx', 'time'])

class MyError(Exception):
	def __init__(self, value):
		self.value = value
	def __str__(self):
		return repr(self.value)

class noSuchLinkException(MyError):
	"""docstring for noSuchLinkException"""


''' START aux functions
		------------------- '''
def calculateDriveTime(path, roads):
	""" calcultaed total time per path, per speed data for the generation-th time sample.
        defined inside to gain access  to roads
    """
	def getTime(junctions):
		''' input: junctions is a touple of junction indices
            defined inside to gain access to generation
        '''
		links = roads[junctions[0]].links
		link = next((x for x in links if x[1] == junctions[1]), None)
		if link == None:
			raise noSuchLinkException(junctions)
		return link.distance / roads.link_speed(link)

	pairs = zip(path, path[1:])
	return sum(map(getTime, pairs))
'''	------------------
    END aux functions '''

def runAssured(source = 1, target = 5, maxTime = 100, confidence = 0.5, total_samples = 50, learning_samples=10, roads=None):
	'''docstring for run_assured'''

	path_set = set() #set of Entries
	if roads == None:
		roads = load_map_from_csv('israel.csv',0,100)

	instances = random.choice(total_samples, learning_samples,  False) #k random values
	for instance in instances:
		roads.generation = instance
		entry = Entry(aStarSearch(source, target, 0, roads))
		path_set.add(entry) #instance is the generation number

	paths = dict(zip(range(len(path_set)), list(path_set))) #dict: idx -> list of entries. one per path
	shortestPath = PathLen(0,-1) #initialize dummy minimum 	

	potentialPaths = []
	for idx, entry in paths.items():
		'''
		go over all paths stored in the dict, 
		for each, get the drive time for each sample.
		if drive time is shorter than maxTime with given confidence level,
		go on to get the average on all running times (still per that path...)
		and compare to the current shortest path.
		'''

		for sampleNum in range(total_samples): #go over all samples - calculate drive time on i-th path.
			entry.times.append(calculateDriveTime(entry.path, roads))

		numTimesUnderThreshold = len([time for time in entry.times if time < maxTime])
		if numTimesUnderThreshold/total_samples < confidence:
			continue # not interested in this path, as it doesn't meet the criteria

		if entry.path not in potentialPaths: #accumulate paths we that were under the threshold time limit
			potentialPaths.append(entry.path)

		average = reduce((lambda x,y:x+y), entry.times)/total_samples
		if average < shortestPath.time or shortestPath.time < 0:
			shortestPath = PathLen(idx, average)
	return (paths[shortestPath.idx].path, potentialPaths)

if __name__ == '__main__':
    print "shortest path is {path}".format(path = runAssured()[0])